package com.atstudent.gmall.order.service.impl;
import java.math.BigDecimal;
import java.util.*;

import com.alibaba.fastjson.JSON;
import com.atstudent.gmall.common.constant.GmallConstant;
import com.atstudent.gmall.common.execption.GmallException;
import com.atstudent.gmall.common.feign.cart.CartFeignClient;
import com.atstudent.gmall.common.feign.interceptor.AuthUserInfo;
import com.atstudent.gmall.common.feign.product.SkuFeignClient;
import com.atstudent.gmall.common.feign.util.AuthUserInfoUtils;
import com.atstudent.gmall.common.feign.ware.WareFeignClient;
import com.atstudent.gmall.common.result.Result;
import com.atstudent.gmall.common.result.ResultCodeEnum;
import com.atstudent.gmall.enums.OrderStatus;
import com.atstudent.gmall.enums.ProcessStatus;
import com.atstudent.gmall.order.dto.DetailDto;
import com.atstudent.gmall.order.dto.OrderSubmitDto;
import com.atstudent.gmall.order.entity.OrderDetail;
import com.atstudent.gmall.order.entity.OrderStatusLog;
import com.atstudent.gmall.order.mapper.OrderStatusLogMapper;
import com.atstudent.gmall.product.entity.SkuInfo;
import com.atstudent.gmall.rabbit.constant.RabbitConstant;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atstudent.gmall.order.entity.OrderInfo;
import com.atstudent.gmall.order.service.OrderInfoService;
import com.atstudent.gmall.order.mapper.OrderInfoMapper;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.stream.Collectors;

/**
* @author xueyoupeng
* @description 针对表【order_info(订单表 订单表)】的数据库操作Service实现
* @createDate 2023-09-16 15:00:31
*/
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo>
    implements OrderInfoService{

    @Autowired
    private RedisTemplate<String , String> redisTemplate;

    @Autowired
    private SkuFeignClient skuFeignClient ;

    @Autowired
    private OrderInfoMapper orderInfoMapper ;

    @Autowired
    private OrderStatusLogMapper orderStatusLogMapper ;

    @Autowired
    private CartFeignClient cartFeignClient ;

    @Autowired
    private WareFeignClient wareFeignClient ;

    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Override
    public String submitOrder(String tradeNo, OrderSubmitDto orderSubmitDto) {

        //判断外部交易号在Redis中所是否存在，如果不存在删除，然后执行后续逻辑(lua脚本实现)
        String script = "if redis.call('exists' , KEYS[1])\n" +
                "then\n" +
                "    return redis.call('del' , KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(GmallConstant.REDIS_ORDER_CONFIRM + tradeNo));
        if (result == 0){
            throw new GmallException(ResultCodeEnum.ORDER_REPT);
        }

        // 校验价格
        List<DetailDto> detailDtoList = orderSubmitDto.getOrderDetailList();
        List<DetailDto> collect = detailDtoList.stream().filter(detailDto -> {
            Long skuId = detailDto.getSkuId();
            //根据skuId远程调用product微服务 查询数据库 商品详情信息
            Result<SkuInfo> skuInfoResult = skuFeignClient.findSkuInfoBySkuId(skuId);
            SkuInfo skuInfo = skuInfoResult.getData();
            //把价格不相等的数据保存在流里面
            boolean comparison = skuInfo.getPrice().doubleValue() != detailDto.getOrderPrice().doubleValue();
            return comparison;
        }).collect(Collectors.toList());

        if (collect.size() > 0){
            throw new GmallException(ResultCodeEnum.SKU_PRICE_ERROR) ;
        }

        // 校验商品是否存在库存
        List<DetailDto> dtoList = detailDtoList.stream().filter(detailDto -> {
            Long skuId = detailDto.getSkuId();
            Integer skuNum = detailDto.getSkuNum();
            String hasStock = wareFeignClient.hasStock(skuId, skuNum);
            return "0".equals(hasStock);
        }).collect(Collectors.toList());
        //说明流里面存在 无库存的商品数据
        if (dtoList.size() > 0){
            throw new GmallException(ResultCodeEnum.SKU_STOCK_ERROR) ;
        }

        // 把订单的相关数据保存到对应数据库表中
        //  保存订单数据
        OrderInfo orderInfo = saveOrderInfo(tradeNo , orderSubmitDto);

        // 保存订单明细数据
        saveOrderDetail(orderInfo , orderSubmitDto);

        // 保存状态日志数据
        saveOrderStatusLog(orderInfo);

        // 远程调用service-cart微服务接口，删除选中的购物车数据
        cartFeignClient.deleteAllChecked();

        //TODO发送延迟消息 关单
        //先准备消息
        HashMap<String, Long> hashMap = new HashMap<>();
        hashMap.put("orderId" , orderInfo.getId());
        hashMap.put("userId" , orderInfo.getUserId());

        //向rabbitmq发送延迟消息
        rabbitTemplate.convertAndSend(RabbitConstant.ORDER_EXCHANGE , RabbitConstant.ORDER_ROUTING_KEY , JSON.toJSONString(hashMap));

        //返回订单id
        return String.valueOf(orderInfo.getId());
    }

    // 关闭订单操作
    public void closeOrder(Long orderId, Long userId) {
        // 根据订单id和用户id查询订单
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OrderInfo::getId , orderId);
        queryWrapper.eq(OrderInfo::getUserId , userId);
        OrderInfo orderInfo = orderInfoMapper.selectOne(queryWrapper);

        //在并发访问的时候会出现问题：判断已经结束，在执行修改订单状态为close的时候，用户进行了支付，
        // 那么此时订单的状态给修改为了已支付，然后在执行if中的代码，订单状态被改成CLOSE
        //判断订单的状态
        if (OrderStatus.UNPAID.name().equals(orderInfo.getOrderStatus()) && ProcessStatus.UNPAID.name().equals(orderInfo.getProcessStatus())){
            //修改订单状态为CLOSE
            orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
            orderInfo.setProcessStatus(ProcessStatus.CLOSED.name());
            // update order_info set order_status = "closed" , process_status = "close" where id = ? and order_status = "UNPAID" and process_status = "UNPAID"
            //拼接的条件
            queryWrapper.eq(OrderInfo::getOrderStatus , OrderStatus.UNPAID.name());
            queryWrapper.eq(OrderInfo::getProcessStatus , ProcessStatus.UNPAID.name());
            orderInfoMapper.update(orderInfo , queryWrapper);
        }
    }

    private void saveOrderStatusLog(OrderInfo orderInfo) {
        OrderStatusLog orderStatusLog = new OrderStatusLog();

        orderStatusLog.setUserId(orderInfo.getUserId());
        orderStatusLog.setOrderId(orderInfo.getId());
        orderStatusLog.setOrderStatus(orderInfo.getOrderStatus());
        orderStatusLog.setOperateTime(orderInfo.getOperateTime());

        orderStatusLogMapper.insert(orderStatusLog);
    }

    private void saveOrderDetail(OrderInfo orderInfo, OrderSubmitDto orderSubmitDto) {

        List<DetailDto> orderDetailList = orderSubmitDto.getOrderDetailList();
        orderDetailList.stream().map(detailDto -> {
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setUserId(orderInfo.getUserId());
            orderDetail.setOrderId(orderInfo.getId());
            orderDetail.setSkuId(detailDto.getSkuId());
            orderDetail.setSkuName(detailDto.getSkuName());
            orderDetail.setImgUrl(detailDto.getImgUrl());
            orderDetail.setOrderPrice(detailDto.getOrderPrice().intValue());
            orderDetail.setSkuNum(String.valueOf(detailDto.getSkuNum()));
            orderDetail.setCreateTime(new Date());
            orderDetail.setSplitTotalAmount(detailDto.getOrderPrice().multiply(new BigDecimal(detailDto.getSkuNum())));
            orderDetail.setSplitActivityAmount(new BigDecimal("0"));
            orderDetail.setSplitCouponAmount(new BigDecimal("0"));

            return orderDetail;
        }).collect(Collectors.toList());
    }


    // 保存订单数据
    private OrderInfo saveOrderInfo(String tradeNo, OrderSubmitDto orderSubmitDto) {

        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setConsignee(orderSubmitDto.getConsignee());
        orderInfo.setConsigneeTel(orderSubmitDto.getConsigneeTel());

        List<DetailDto> detailDtoList = orderSubmitDto.getOrderDetailList();
        Optional<BigDecimal> totalAmount = detailDtoList.stream().map(detailDto -> detailDto.getOrderPrice().multiply(new BigDecimal(detailDto.getSkuName())))
                .reduce((o1, o2) -> o1.add(o2));
        orderInfo.setTotalAmount(totalAmount.get());

        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());

        AuthUserInfo authUserInfo = AuthUserInfoUtils.getAuthUserInfo();
        String userId = authUserInfo.getUserId();
        orderInfo.setUserId(Long.parseLong(userId));

        orderInfo.setPaymentWay("ONLINE");
        orderInfo.setDeliveryAddress(orderSubmitDto.getDeliveryAddress());
        orderInfo.setOrderComment(orderSubmitDto.getOrderComment());
        orderInfo.setOutTradeNo(tradeNo);

        DetailDto detailDto = detailDtoList.get(0);
        orderInfo.setTradeBody(detailDto.getSkuName());

        orderInfo.setCreateTime(new Date());

        long expireTime = System.currentTimeMillis() + 1000 * 60 * 30;
        orderInfo.setExpireTime(new Date(expireTime));

        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());

        orderInfo.setImgUrl(detailDto.getImgUrl());

        orderInfo.setOperateTime(new Date());
        orderInfo.setActivityReduceAmount(new BigDecimal("0"));
        orderInfo.setCouponAmount(new BigDecimal("0"));
        orderInfo.setOriginalTotalAmount(totalAmount.get());
        orderInfo.setFeightFee(new BigDecimal("0"));
        orderInfo.setRefundableTime(new Date());

        orderInfoMapper.insert(orderInfo);

        return orderInfo;
    }
}




